/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// promise状态
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
/**
 * 完整版 Promise 实现链式调用，all finally等细节
 * 符合 Promise A+ 规范
 * 
 * @class MyPromise
 */
class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject); // 先执行promise中的同步代码
    } catch (e) {
      this.reject(e); // 同步代码执行中如果遇到了异常，则reject
    }
  }
  // 初始promise状态
  status = PENDING;
  // 成功的值
  value = undefined;
  // 失败的原因
  reason = undefined;
  // 成功回调(数组形式，因为)
  successCallback = [];
  // 失败回调
  failCallback = [];

  resolve = (value) => {
    // 只能PENDING->FULFILLED，如果不是PENDING，则无法再继续
    this.status = FULFILLED;
    this.value = value;
    // 调用成功回调，shift调用时，successCallback数组自动长度减少(类似队列结构)
    while (this.successCallback.length) this.successCallback.shift()();
  };
  reject = (reason) => {
    // 只能PENDING->REJECTED，如果不是PENDING，则无法再继续
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    // 失败原因
    this.reason = reason;
    //  调用成功回调，shift调用时，successCallback数组自动长度减少(类似队列结构)
    while (this.failCallback.length) this.failCallback.shift()();
  };
  then(successCallback, failCallback) {
    successCallback = successCallback ? successCallback : (value) => value; // 如果then传入的是值，则构造出一个函数,传到下一个then，类似.then(2).then(4)
    // 参数可选
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason;
        }; // 如果错误回调，
    // then返回的也是Promise对象
    let promsie2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // then中可能有异步任务，需要延时执行，
        setTimeout(() => {
          try {
            const x = successCallback(this.value);
            resolvePromise(promsie2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = failCallback(this.reason);
            resolvePromise(promsie2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              const x = successCallback(this.value);
              resolvePromise(promsie2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              const x = failCallback(this.reason);
              resolvePromise(promsie2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promsie2;
  }
  // 不论是成功还是回调都会执行
  finally(callback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }
  // 接收then抛出的异常，resolve值为undefined，返回的也是promise对象
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }
  static all(array) {
    let result = [];
    let index = 0; // 记录all中的所有异步任务处理完毕
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          current.then(
            (value) => addData(i, value),
            (reason) => reject(reason)
          );
        } else {
          addData(i, array[i]);
        }
      }
    });
  }
  // resolve(将值使用promise进行包装，且属于resolve回调)
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve) => resolve(value));
  }
}

function resolvePromise(promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError("发生promise循环调用"));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}
